#include "PageCache.h"

PageCache PageCache::_sInst;

// 获取一个k页的span
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0 && k < NPAGES);

	// 不能用桶锁，因为如果k桶没有对象则要把k ~ 128都遍历一遍，桶锁是锁不住的，效率会比较低（涉及到频繁的加锁解锁问题）
	// 锁的话因为递归调用自己的问题，所以会死锁，两个解决方案：1、递归锁 2、分离一个子函数出来（弄成非递归式）


	// 先检查第k个桶里面有没有span,有的话就弹一个出去
	if (!_spanLists[k].Empty())
	{
		return _spanLists->PopFront();
	}

	// 检查一下后面的桶里面有没有span，如果有可以把它进行切分
	for (size_t i = k + 1; i < NPAGES; ++i) 
	{
		if (!_spanLists[i].Empty())
		{
			Span* nSpan = _spanLists[i].PopFront();
			Span* kSpan = new Span;

			// 在nSpan的头部切一个k页下来
			// k页span返回
			// nspan再挂到对应映射的位置
			kSpan->_pageId = nSpan->_pageId;
			kSpan->_n = k;

			nSpan->_pageId += k;
			nSpan->_n -= k;

			// 切走了k页还剩x页，所以要挂回x页的位置：
			_spanLists[nSpan->_n].PushFront(nSpan);

			return kSpan;
		}
	}

	// 走到这个位置就说明后面没有大页的span了
	// 这时就要去找堆要一个128页的span
	Span* bigSpan = new Span;
	void* ptr = SystemAlloc(NPAGES - 1);
	
	// 知道地址算页号：飞书
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	bigSpan->_n = NPAGES - 1; // 因为申请的就是128页,但是不要直接写128，因为万一你想把桶加大呢？

	_spanLists[bigSpan->_n].PushFront(bigSpan);

	return NewSpan(k); // 再递归的调用自己即可

	// 这里可能会有一个小问题，就是有人说想直接复用上面的逻辑，因为递归调用慢了，还要从k走到128
	// 虽然确实会慢一点，但是也就一百来次，计算机太快了，所以几乎都可以忽略不计,所以不用去考虑这种芝麻量的事
	// 所以代码的复用很重要，因为如果某个细节错乱，那两个地方还都得改动
}

